package com.gitee.dupe.oauth.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.gitee.dolt.boot.decrypt.PasswordEncoder;
import com.gitee.dolt.cache.repository.CacheOps;
import com.gitee.dolt.core.cache.model.CacheKey;
import com.gitee.dolt.core.constant.RoleConstant;
import com.gitee.dolt.core.util.BizAssert;
import com.gitee.dolt.mail.service.MailService;
import com.gitee.dolt.security.model.AuthInfo;
import com.gitee.dolt.security.model.SysUser;
import com.gitee.dolt.security.token.TokenProvider;
import com.gitee.dolt.utils.StrUtils;
import com.gitee.dupe.authority.dto.MenuDTO;
import com.gitee.dupe.authority.entity.Resource;
import com.gitee.dupe.authority.entity.User;
import com.gitee.dupe.authority.enumeration.ResourceTypeEnum;
import com.gitee.dupe.authority.enumeration.UserStateEnum;
import com.gitee.dupe.authority.service.IResourceService;
import com.gitee.dupe.authority.service.IRoleService;
import com.gitee.dupe.authority.service.IUserService;
import com.gitee.dupe.common.cache.ResetPasswordCodeKeyBuilder;
import com.gitee.dupe.oauth.dto.GetResetPasswordCodeDTO;
import com.gitee.dupe.oauth.dto.ResetPasswordByCodeDTO;
import com.gitee.dupe.oauth.enumeration.LoginNameType;
import com.gitee.dupe.oauth.service.OauthService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author LYH
 */
@Service
@RequiredArgsConstructor
public class OauthServiceImpl implements OauthService {
    private final ResetPasswordCodeKeyBuilder resetPasswordCodeKeyBuilder = new ResetPasswordCodeKeyBuilder();
    private final IUserService userService;
    private final CacheOps cacheOps;
    private final MailService mailService;
    private final PasswordEncoder passwordEncoder;
    private final TokenProvider tokenProvider;
    private final IRoleService roleService;
    private final IResourceService resourceService;

    @Override
    public void getResetPasswordCode(GetResetPasswordCodeDTO getResetPasswordCodeDto) {
        String loginName = getResetPasswordCodeDto.getLoginName();
        LoginNameType loginNameType = getResetPasswordCodeDto.getLoginNameType();
        switch (getResetPasswordCodeDto.getLoginNameType()) {
            case EMAIL:
                User user = userService.getOneByEmail(loginName);
                BizAssert.notNull(user, "邮箱不存在");
                BizAssert.isTrue(!UserStateEnum.LOCK.equals(user.getState()), "用户已锁定");
                CacheKey key = resetPasswordCodeKeyBuilder.key(loginNameType.name(), loginName);
                BizAssert.isTrue(!cacheOps.exists(key), "验证码发送过于频繁");
                URL url = ResourceUtil.getResource("templates/reset_password_email.txt");
                List<String> lines = FileUtil.readUtf8Lines(url);
                StringBuilder sb = StrUtil.builder();
                for (int i = 2; i < lines.size(); i++) {
                    sb.append(lines.get(i)).append(StrUtil.LF);
                }
                String subject = lines.get(0);
                String code = RandomUtil.randomString(8);
                String content = StrUtil.format(sb.toString(), code);
                ThreadUtil.execute(() -> mailService.sendEmail(loginName, subject, content));
                cacheOps.set(key, 1);
                cacheOps.set(resetPasswordCodeKeyBuilder.key(getResetPasswordCodeDto.getKey()),
                        StrUtil.format("{}-{}", code, user.getId()));
                break;
            default:
                BizAssert.fail("不支持的用户名类型");
        }
    }

    @Override
    public void resetPasswordByCode(ResetPasswordByCodeDTO resetPasswordByCodeDTO) {
        CacheKey key = resetPasswordCodeKeyBuilder.key(resetPasswordByCodeDTO.getKey());
        String value = cacheOps.get(key, false);
        BizAssert.notNull(value, "验证码已失效");
        String[] temp = StrUtil.split(value, "-");
        String code = temp[0];
        String userId = temp[1];
        BizAssert.isTrue(code.equalsIgnoreCase(resetPasswordByCodeDTO.getCode()), "验证码错误");
        cacheOps.del(key);
        User user = userService.getById(userId);
        BizAssert.notNull(user, "用户信息已丢失");
        user.setPasswordHash(passwordEncoder.encode(resetPasswordByCodeDTO.getNewPassword()));
        userService.updateById(user);
    }

    @Override
    public SysUser getUserInfo(String token) {
        AuthInfo authInfo = tokenProvider.getAuthInfo(token);
        Long userId = authInfo.getUser().getId();
        User user = userService.getById(userId);
        List<String> roles = new ArrayList<>();
        List<String> roleNames = new ArrayList<>();
        List<String> resources;
        List<Resource> menuResources;
        roleService.findAllEnabledByUserId(userId).forEach(e -> {
            roles.add(e.getRoleIdent());
            roleNames.add(e.getRoleName());
        });
        if (roles.contains(RoleConstant.SUPER_ADMIN)) {
            // 超管给所有权限
            resources = Arrays.asList("*");
            menuResources = resourceService.findAllByType(ResourceTypeEnum.MENU);
        } else {
            List<Resource> userResources = resourceService.findAllEnabledByUserId(userId);
            resources = userResources.stream().map(Resource::getIdent)
                    .filter(StrUtil::isNotEmpty).flatMap(e -> Arrays.stream(StrUtil.split(e, StrUtils.SEMICOLON)))
                    .collect(Collectors.toList());
            menuResources = userResources.stream().filter(e -> ResourceTypeEnum.MENU.equals(e.getType()))
                    .collect(Collectors.toList());
        }
        List<MenuDTO> menus = menuResources.stream().map(e -> new MenuDTO().setId(e.getId()).setName(e.getName())
                .setSort(e.getSort()).setIcon(e.getIcon()).setUrl(e.getUrl()).setParentId(e.getParentId())
                .setExternalLink(e.getExternalLink())).collect(Collectors.toList());
        SysUser sysUser = new SysUser().setId(user.getId()).setAccount(user.getAccount()).setName(user.getRealName())
                .setScope(user.getGroup()).setRoles(roles).setResources(resources);
        sysUser.set("roleNames", roleNames);
        sysUser.set("avatar", user.getAvatar());
        sysUser.set("mobile", user.getMobile());
        sysUser.set("email", user.getEmail());
        sysUser.set("group", user.getGroup());
        sysUser.set("menus", menus);
        return sysUser;
    }
}
